<script lang="ts" setup>
import { ref, computed, watch } from 'vue'
import Editor from '@tinymce/tinymce-vue'
import type { Editor as TinyMCEEditor, EditorEvent } from 'tinymce'
import { getToken, csrfToken as getCsrfToken } from '@/utils/auth'

// Import TinyMCE plugins and resources
import 'tinymce/tinymce'
import 'tinymce/icons/default/icons'
import 'tinymce/themes/silver'
import 'tinymce/plugins/advlist'
import 'tinymce/plugins/anchor'
import 'tinymce/plugins/autolink'
import 'tinymce/plugins/autoresize'
import 'tinymce/plugins/autosave'
import 'tinymce/plugins/charmap'
import 'tinymce/plugins/code'
import 'tinymce/plugins/codesample'
import 'tinymce/plugins/directionality'
import 'tinymce/plugins/emoticons'
import 'tinymce/plugins/fullscreen'
import 'tinymce/plugins/image'
import 'tinymce/plugins/importcss'
import 'tinymce/plugins/insertdatetime'
import 'tinymce/plugins/link'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/media'
import 'tinymce/plugins/nonbreaking'
import 'tinymce/plugins/pagebreak'
import 'tinymce/plugins/preview'
import 'tinymce/plugins/quickbars'
import 'tinymce/plugins/save'
import 'tinymce/plugins/searchreplace'
import 'tinymce/plugins/table'
import 'tinymce/plugins/visualblocks'
import 'tinymce/plugins/visualchars'
import 'tinymce/plugins/wordcount'

const VITE_APP_SERVER_API = import.meta.env.VITE_API_URL

interface EditorProps {
  class?: string
  modelValue?: string
  options?: {
    height?: number
    [key: string]: unknown
  }
}

interface EditorEvents {
  (e: 'update:modelValue', value: string): void
  (e: 'input', value: string): void
  (e: 'change', value: string): void
  (e: 'onInit', event: unknown): void
  (e: 'onChange', event: EditorEvent<unknown>): void
  (e: 'onFocus', event: EditorEvent<unknown>): void
  (e: 'onBlur', event: EditorEvent<unknown>): void
}

const props = withDefaults(defineProps<EditorProps>(), {
  class: '',
  modelValue: '',
  options: () => ({}),
})

const emit = defineEmits<EditorEvents>()

const hasChange = ref(false)
const hasInit = ref(false)
const loading = ref(true)
const html = ref(props.modelValue)

const onChange = (event: EditorEvent<unknown>) => {
  const editor = event.target as TinyMCEEditor
  const editorContent = editor.getContent()
  html.value = editorContent
  emit('update:modelValue', editorContent)
  emit('onChange', event)
}

const onInit = (event: unknown) => {
  emit('onInit', event)
}

const onFocus = (event: EditorEvent<unknown>) => {
  emit('onFocus', event)
}

const onBlur = (event: EditorEvent<unknown>) => {
  emit('onBlur', event)
}

watch(
  () => props.modelValue,
  (newVal) => {
    if (newVal !== html.value) {
      html.value = newVal ?? ''
    }
  },
)

const init = computed(() => ({
  license_key: 'gpl',
  anchor_bottom: '#mybottom',
  cache_suffix: '?v=7.9.1',
  deprecation_warnings: false,
  base_url: '/tinymce',
  language_url: '/tinymce/langs/zh_CN.js',
  language: 'zh_CN',
  branding: false,
  promotion: false,
  skin_url: '/tinymce/skins/ui/oxide',
  emoticons_database_url: '/tinymce/plugins/emoticons/emojis.min.js',
  emoticons_append: {},
  content_css: [
    '/tinymce/skins/content/default/normalize.min.css',
    '/tinymce/skins/content/default/ck_htmledit_views.min.css',
  ],
  body_class: 'htmledit_views',
  external_plugins: {
    formatpainter: '/tinymce/plugins/formatpainter/plugin.js',
    layout: '/tinymce/plugins/layout/plugin.js',
    letterspacing: '/tinymce/plugins/letterspacing/plugin.js',
    table: '/tinymce/plugins/table/plugin.js',
  },
  plugins: [
    'preview',
    'layout',
    'importcss',
    'searchreplace',
    'autolink',
    'autosave',
    'save',
    'directionality',
    'visualblocks',
    'visualchars',
    'fullscreen',
    'image',
    'link',
    'media',
    'code',
    'codesample',
    'table',
    'charmap',
    'pagebreak',
    'nonbreaking',
    'anchor',
    'insertdatetime',
    'advlist',
    'lists',
    'wordcount',
    'formatpainter',
    'charmap',
    'quickbars',
    'emoticons',
    'indent2em',
  ],
  mobile: {
    plugins:
      'preview layout importcss searchreplace autolink autosave save directionality visualblocks visualchars fullscreen image link media code codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount formatpainter charmap quickbars emoticons advtable',
  },
  menubar: 'file edit view insert format tools table tc help',
  toolbar:
    'undo redo formatselect formatpainter a11ycheck fontselect fontsizeselect bold italic underline strikethrough subscript superscript removeformat forecolor backcolor alignleft aligncenter alignright alignjustify bullist numlist outdent indent indent2em lineheight lists image media link table layout fullscreen',
  autosave_ask_before_unload: true,
  autosave_interval: '30s',
  autosave_prefix: '{path}{query}-{id}-',
  autosave_restore_when_empty: false,
  autosave_retention: '2m',
  image_advtab: true,
  link_list: [
    { title: 'My page 1', value: 'http://www.tinymce.com' },
    { title: 'My page 2', value: 'http://www.moxiecode.com' },
  ],
  image_list: [
    { title: 'My page 1', value: 'http://www.tinymce.com' },
    { title: 'My page 2', value: 'http://www.moxiecode.com' },
  ],
  image_class_list: [
    { title: 'None', value: '' },
    { title: 'Some class', value: 'class-name' },
  ],
  importcss_append: true,
  template_cdate_format: '[Date Created (CDATE): %m/%d/%Y : %H:%M:%S]',
  template_mdate_format: '[Date Modified (MDATE): %m/%d/%Y : %H:%M:%S]',
  height: props.options.height || 660,
  image_caption: true,
  quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
  noneditable_noneditable_class: 'mceNonEditable',
  toolbar_mode: 'floating',
  spellchecker_ignore_list: ['Ephox', 'Moxiecode'],
  content_style: '.mymention{ color: gray; }',
  contextmenu: ['link', 'image', 'table'],
  a11y_advanced_options: true,
  font_formats:
    '微软雅黑=Microsoft YaHei; 宋体=SimSun; 新宋体=NSimSun; 仿宋=FangSong; 楷体=KaiTi; 黑体=SimHei; Arial=arial,helvetica,sans-serif; Arial Black=arial black,avant garde; Book Antiqua=book antiqua,palatino; Comic Sans MS=comic sans ms,sans-serif; Courier New=courier new,courier; Georgia=georgia,palatino; Helvetica=helvetica; Impact=impact,chicago; Symbol=symbol; Tahoma=tahoma,arial,helvetica,sans-serif; Times New Roman=times new roman,times; Verdana=verdana,geneva;',
  fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 60px 72px',
  file_picker_types: 'media,image,file',
  setup: (editor: TinyMCEEditor) => {
    console.log(`ID为:${editor.id}的编辑器即将初始化.`)
  },
  init_instance_callback: (editor: TinyMCEEditor) => {
    console.log(`ID为:${editor.id}的编辑器已初始化完成.`)
    if (html.value) {
      editor.setContent(html.value)
    }
    hasInit.value = true
    editor.on('NodeChange Change KeyUp SetContent', () => {
      hasChange.value = true
      emit('update:modelValue', editor.getContent())
      emit('input', editor.getContent())
      emit('change', editor.getContent())
    })
    loading.value = false
  },
  images_upload_url: VITE_APP_SERVER_API + '/api/system/upload',
  images_upload_base_path: VITE_APP_SERVER_API,
  images_upload_credentials: true,
  automatic_uploads: true,
  images_upload_handler: (
    blobInfo: { blob: () => Blob; filename: () => string },
    progress: (percent: number) => void,
  ) => {
    return new Promise((resolve, reject) => {
      const token = getToken() || localStorage.getItem('access_token')
      const csrfToken = getCsrfToken()
      const xhr = new XMLHttpRequest()

      xhr.withCredentials = false
      xhr.open('POST', `${VITE_APP_SERVER_API}/api/system/upload`)
      xhr.withCredentials = true
      xhr.setRequestHeader('authorization', 'Bearer ' + token)
      xhr.setRequestHeader('x-csrf-token', csrfToken + '')

      xhr.upload.onprogress = (e) => {
        progress((e.loaded / e.total) * 100)
      }

      xhr.onload = () => {
        if (xhr.status === 403) {
          reject({ message: 'HTTP Error: ' + xhr.status, remove: true })
          return
        }

        if (xhr.status < 200 || xhr.status >= 300) {
          reject('HTTP Error: ' + xhr.status)
          return
        }

        try {
          const json = JSON.parse(xhr.responseText)
          if (!json || json.code !== 200) {
            reject('Invalid JSON: ' + xhr.responseText)
            return
          }
          resolve(VITE_APP_SERVER_API + json.data.url)
        } catch (e) {
          console.log('JSON parse error: ', e)
          reject('Failed to parse JSON response')
        }
      }

      xhr.onerror = () => {
        reject('Image upload failed due to a XHR Transport error. Code: ' + xhr.status)
      }

      const formData = new FormData()
      formData.append('file', blobInfo.blob(), blobInfo.filename())
      xhr.send(formData)
    })
  },
}))
</script>

<template>
  <div
    v-loading="loading"
    :style="{ minHeight: `${options.height || init.height}px` }"
    class="tinymce-eittor-box"
  >
    <Editor
      v-model="html"
      api-key="udm8u7u1w88b8yqqt0czgf3glqzet1mnbt95at9wv8u6bib3"
      :init="init"
      @onInit="onInit"
      @onChange="onChange"
      @onFocus="onFocus"
      @onBlur="onBlur"
    />
  </div>
</template>
